#include "texture.h"
#include "ImageLoader.h"
#include <string>
#include <vector>



#ifdef _DEBUG
#include <iostream>
#endif

Texture::Texture()
{
	init();
}

struct rgba
{
public:
	unsigned char r,g,b,a;
	rgba(){r=0; g=0; b=0; a=0;} // default
	rgba(unsigned char red, unsigned char green, unsigned char blue, unsigned char alpha) 
		: r(red), g(green), b(blue), a(alpha) {} 
};

void Texture::init()
{
	glGenTextures(1, &_handle);
	#ifdef _DEBUG
		std::cout << "tex created!\n" << std::endl;
	#endif
}

Texture::Texture( char *fileName )
{
	init();

	bool filtering = true;
	bool flipY = false;

	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

	std::vector<unsigned char> image;
	unsigned long width, height;
	int error = ImageLoader::loadImagePNG(image, width, height, std::string(fileName) );
	_width = width;
	_height = height;
	//if there's an error, display it
	if(error != 0)
	{
	#ifdef DEBUG
		std::cout << "Error: PicoPNG failed to load image " << fileName << std::endl;
	#endif
	}
	else
	{
#ifdef DEBUG
		std::cout << "PicoPNG loaded image " << fileName << " \t " << width << "x" <<  height << std::endl;
#endif
		if( flipY )
		{
			// Pack pixels into RGBA and flip Y
			std::vector< std::vector<rgba> > pixels = 
				std::vector<std::vector<rgba>>( height, std::vector<rgba>
				( width, rgba(255,0,0,0) ) );

			// Read pixel data stream into a 2x2 array for easier access.
			size_t w = width-1;
			size_t h = height-1;
			size_t i = 0;
			for(size_t y=0; y<height; y++)
			{
				for(size_t x=0; x<width; x++)
				{
					unsigned char r = image[i++];
					unsigned char g = image[i++];
					unsigned char b = image[i++];
					unsigned char a = image[i++];
					pixels[y][x] = rgba( r,g,b,a );
				}
			}

			//size_t h = height-1;
			// How im used to dealing with pixel data:
			//pixels[0][0].r = 255;
			//pixels[0][0].g = 0;
			//pixels[0][0].b = 0;

			i = 0;
			// Read into image with y coord flipped as OpenGL expects.
			for(size_t y=0; y<height; y++)
			{
				for(size_t x=0; x<width; x++)
				{
					image[i++] = pixels[h-y][x].r;
					image[i++] = pixels[h-y][x].g;
					image[i++] = pixels[h-y][x].b;
					image[i++] = pixels[h-y][x].a;
				}
			}
		}

		glBindTexture(GL_TEXTURE_2D, _handle);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, &image[0] );

		if(filtering)
		{
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
		}
		else
		{
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
		}

		//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // GL_REPEAT,GL_CLAMP, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_BORDER
		//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);

		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
	}
}

Texture::Texture(size_t width, size_t height)
{
	createEmptyTexture(width,height);
}

Texture::~Texture()
{
	glDeleteTextures(1, &_handle);
	_handle = 0;
}


void Texture::createEmptyTexture(size_t width, size_t height)
{
	_width = width;
	_height = height;
	glGenTextures(1, &_handle);
	glBindTexture(GL_TEXTURE_2D, _handle);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); // automatic mipmap
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, _width, _height, 0,
		GL_RGBA, GL_UNSIGNED_BYTE, 0x0);
	glBindTexture(GL_TEXTURE_2D, 0);
}